﻿using Ln.Rbac.Application;
using Ln.Rbac.Application.Contracts;
using Ln.Rbac.Domain;
using Ln.Rbac.Domain.Authorization;
using Ln.Rbac.Domain.Shared;
using Ln.Rbac.Domain.Shared.Consts;
using Ln.Rbac.Domain.Shared.Options;
using Ln.Rbac.SqlsugarCore;
using Jyt.Mes.Application;
using Jyt.Mes.Application.Contracts;
using Jyt.Mes.Domain;
using Jyt.Mes.Domain.Shared;
using Jyt.Mes.SqlSugarCore;

namespace Ln.Core.Web;

[DependsOn(typeof(AbpAuditingModule), typeof(AbpAspNetCoreAuthenticationJwtBearerModule))]
[DependsOn(typeof(AbpSwashbuckleModule), typeof(AbpAspNetCoreSerilogModule))]
[DependsOn(typeof(AbpAspNetCoreMultiTenancyModule), typeof(AbpAspNetCoreMvcModule), typeof(AbpAutofacModule))]
[DependsOn(typeof(LnFramAspNetCoreModule))]
[DependsOn(typeof(LnRbacDomainSharedModule), typeof(LnRbacDomainModule), typeof(LnRbacSqlSugarCoreModule), typeof(LnRbacApplicationContractsModule), typeof(LnRbacApplicationModule))]
[DependsOn(typeof(JytMesDomainSharedModule), typeof(JytMesDomainModule), typeof(JytMesSqlSugarCoreModule), typeof(JytMesApplicationContractsModule), typeof(JytMesApplicationModule))]
public class LnCoreWebModule : AbpModule
{
	#region 成员属性
	private const string DefaultCorsPolicyName = "Default";
	private const string DefaultWwwRoot = $"/api/yi-app/wwwroot";

	private List<AbpModuleInfo> ModuleInfos { get; } = [];
	#endregion

	#region 模块配置
	private Task InitModuleInfos()
	{
		ModuleInfos.Clear();
		ModuleInfos.AddRange(new[]
			{
				new AbpModuleInfo { ModuleType = typeof(LnRbacApplicationModule), ServiceName = "rbac", RootPath = "app" },
				new AbpModuleInfo { ModuleType = typeof(JytMesApplicationModule), ServiceName = "jyt_mes", RootPath = "jyt/mes" },
			}
		);
		return Task.CompletedTask;
	}
	#endregion

	#region 重写方法
	public override Task ConfigureServicesAsync(ServiceConfigurationContext context)
	{
		InitModuleInfos();
		var configuration = context.Services.GetConfiguration();
		Configure<AbpAspNetCoreMvcOptions>(ConfigureMvcOption);
		ConfigureDistributedCache();
		AddCorsAndConfigureTenant(context, configuration);
		AddRateLimiter(context);
		JwtBearer(context, configuration);
		return Task.CompletedTask;
	}

	public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
	{
		var app = context.GetApplicationBuilder();
		if (!context.GetEnvironment().IsDevelopment()) app.UseRateLimiter(); /*开发环境速率限制*/
		app.UseRouting().UseCors(DefaultCorsPolicyName).UseAuthentication() /*启用路由、跨域、无感Token刷新鉴权*/
			.UseMultiTenancy().UseLnSwagger().UseLnApiHandlinge() /*多租户、启用Swagger、请求处理*/
			.UseStaticFiles(DefaultWwwRoot).UseDefaultFiles().UseDirectoryBrowser(DefaultWwwRoot) /*静态资源加载、默认文件、目录浏览*/
			.UseUnitOfWork().UseAuthorization().UseAuditing() /*工作单元、授权、审计日志*/
			.UseAbpSerilogEnrichers().UseConfiguredEndpoints(); /*日志记录、终节点*/
		app.UseRefreshToken();
		return Task.CompletedTask;
	}
	#endregion

	#region 自身方法
	private void ConfigureMvcOption(AbpAspNetCoreMvcOptions options)
	{
		ModuleInfos.ForEach(info =>
		{
			options.ConventionalControllers.Create(info.ModuleType.Assembly, o =>
			{
				o.RemoteServiceName = info.ServiceName;
				o.RootPath = info.RootPath;
			});
		});
	}

	private void ConfigureDistributedCache()
	{
		Configure<AbpAuditingOptions>(options =>
		{
			options.IsEnabled = false;
			options.AlwaysLogSelectors.Add(x => Task.FromResult(true));
		});
		Configure<AbpExceptionHandlingOptions>(options => { options.SendExceptionsDetailsToClients = true; });
		Configure<JsonOptions>(options =>
		{
			options.JsonSerializerOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver();
			options.JsonSerializerOptions.Converters.Add(new DatetimeJsonConverter());
			options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
		});
		//设置缓存不要过期，默认滑动20分钟
		Configure<AbpDistributedCacheOptions>(cacheOptions =>
		{
			cacheOptions.GlobalCacheEntryOptions.SlidingExpiration = null;
			cacheOptions.KeyPrefix = "Ln:";
		});
		Configure<AbpAntiForgeryOptions>(options => { options.AutoValidate = false; });
	}

	private void AddCorsAndConfigureTenant(ServiceConfigurationContext context, IConfiguration configuration)
	{
		//Swagger
		context.Services.AddLnSwaggerGen<LnCoreWebModule>(options =>
		{
			options.SwaggerDoc("default",
				new OpenApiInfo { Title = "Ln.Framework.Abp", Version = "v1", Description = "集大成者" });
		});
		//跨域
		context.Services.AddCors(options =>
		{
			options.AddPolicy(DefaultCorsPolicyName, builder =>
			{
				builder.WithOrigins(configuration["App:CorsOrigins"]!.Split(";", StringSplitOptions.RemoveEmptyEntries)
						.Select(o => o.RemovePostFix("/")).ToArray())
					.WithAbpExposedHeaders().SetIsOriginAllowedToAllowWildcardSubdomains().AllowAnyHeader().AllowAnyMethod()
					.AllowCredentials();
			});
		});
		//配置多租户
		/*Configure<AbpTenantResolveOptions>(options =>
		{
			options.TenantResolvers.Clear();
			options.TenantResolvers.Add(new HeaderTenantResolveContributor());
		});*/
	}

	private void AddRateLimiter(ServiceConfigurationContext context)
	{
		//速率限制，每60秒限制100个请求，滑块添加，分6段
		context.Services.AddRateLimiter(x =>
		{
			x.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
			x.OnRejected = (ctx, t) =>
			{
				if (ctx.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
					ctx.HttpContext.Response.Headers.RetryAfter =
						((int)retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo);
				ctx.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
				ctx.HttpContext.Response.WriteAsync("Too many requests. Please try again later.", t);
				return new ValueTask();
			};
			//全局使用，链式表达式
			x.GlobalLimiter = PartitionedRateLimiter.CreateChained(PartitionedRateLimiter.Create<HttpContext, string>(
				httpContext =>
				{
					var userAgent = httpContext.Request.Headers.UserAgent.ToString();
					return RateLimitPartition.GetSlidingWindowLimiter(userAgent, _ => new SlidingWindowRateLimiterOptions
					{
						PermitLimit = 1000,
						Window = TimeSpan.FromSeconds(60),
						SegmentsPerWindow = 6,
						QueueProcessingOrder = QueueProcessingOrder.OldestFirst
					});
				}));
		});
	}

	private void JwtBearer(ServiceConfigurationContext context, IConfiguration configuration)
	{
		//jwt鉴权
		var jwtOptions = configuration.GetSection(nameof(JwtOptions)).Get<JwtOptions>();
		var refreshJwtOptions = configuration.GetSection(nameof(RefreshJwtOptions)).Get<RefreshJwtOptions>();
		context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
		{
			options.TokenValidationParameters = new TokenValidationParameters
			{
				ClockSkew = TimeSpan.Zero,
				ValidateIssuerSigningKey = true,
				ValidIssuer = jwtOptions.Issuer,
				ValidAudience = jwtOptions.Audience,
				IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SecurityKey))
			};
			options.Events = new JwtBearerEvents
			{
				OnMessageReceived = obj =>
				{
					var accessToken = obj.Request.Query["access_token"];
					if (!string.IsNullOrEmpty(accessToken))
						obj.Token = accessToken;
					return Task.CompletedTask;
				}
			};
		}).AddJwtBearer(TokenTypeConst.Refresh, options =>
		{
			options.TokenValidationParameters = new TokenValidationParameters
			{
				ClockSkew = TimeSpan.Zero,
				ValidateIssuerSigningKey = true,
				ValidIssuer = refreshJwtOptions.Issuer,
				ValidAudience = refreshJwtOptions.Audience,
				IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(refreshJwtOptions.SecurityKey))
			};
			options.Events = new JwtBearerEvents
			{
				OnMessageReceived = o =>
				{
					var token = o.Request.Headers["refresh_token"];
					if (!string.IsNullOrEmpty(token))
					{
						o.Token = token;
						return Task.CompletedTask;
					}
					var refreshToken = o.Request.Query["refresh_token"];
					if (!string.IsNullOrEmpty(refreshToken))
						o.Token = refreshToken;
					return Task.CompletedTask;
				}
			};
		});
		context.Services.AddAuthorization(); //授权
	}
	#endregion
}